import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import aima.core.agent.Action;
import aima.core.agent.impl.DynamicAction;
import aima.core.search.framework.GraphSearch;
import aima.core.search.framework.Problem;
import aima.core.search.framework.Search;
import aima.core.search.framework.SearchAgent;
import aima.core.search.framework.TreeSearch;
import aima.core.search.informed.AStarSearch;
import aima.core.search.uninformed.BreadthFirstSearch;
import aima.core.search.uninformed.DepthLimitedSearch;
import aima.core.search.uninformed.IterativeDeepeningSearch;
import aima.core.util.datastructure.XYLocation;


public class EstadoRetoAtasco {
	private HashMap<String,Coche> coches;
	private XYLocation tamTab;

	//Variables generales para los movimientos
	Coche actual;
	XYLocation posi;


	// Acciones posibles (actions) como objetos DynamicAction

	public static Action Rd = new DynamicAction("Rd"); //Rojo derecha
	public static Action Ri = new DynamicAction("Ri"); //Rojo izquierda
	public static Action Va = new DynamicAction("Va"); //Rojo derecha
	public static Action Vv = new DynamicAction("Vv"); //Rojo izquierda


	public EstadoRetoAtasco()
	{
		//Las posiciones empiezan en 1
		tamTab = new XYLocation(6,6);
		coches = new HashMap<String,Coche>();
		coches.put("R", new Coche(new XYLocation(3,3),1,2));
		coches.put("A", new Coche(new XYLocation(3,2),1,2));
		coches.put("Z", new Coche(new XYLocation(5,5),1,2));
		
		coches.put("V", new Coche(new XYLocation(5,1),2,1));
		coches.put("S", new Coche(new XYLocation(5,3),2,1));
		coches.put("G", new Coche(new XYLocation(4,5),2,1));
		
		coches.put("C1", new Coche(new XYLocation(6,1),3,1));
		coches.put("C2", new Coche(new XYLocation(3,4),3,1));
	}
	
	public EstadoRetoAtasco(EstadoRetoAtasco mc) {
	this(mc.getCoches(),mc.getTamTab());
	}
	
	public EstadoRetoAtasco(HashMap<String,Coche> coches,XYLocation tamTab) {
	this.coches = coches;
	this.tamTab = tamTab;
	}
	
	public HashMap<String,Coche> getCoches() {
		return coches;
	}
	public XYLocation getTamTab() {
		return tamTab;
	}
	
	public XYLocation getPosicionOf(String iden)
	{
		return (coches.get(iden)).getPosicion();
	}
	

	
	//Coche Rojo
	public void moveRd() {
		actual = coches.get("R");
		actual.setPosicion(actual.getPosicion().right());
		coches.put("R", actual);
	}
	public void moveRi() {
		actual = coches.get("R");
		actual.setPosicion(actual.getPosicion().left());
		coches.put("R", actual);
	}
	//Coche Verde
	public void moveVa() {
		actual = coches.get("V");
		actual.setPosicion(actual.getPosicion().up());
		coches.put("V", actual);
	}
	public void moveVv() {
		actual = coches.get("V");
		actual.setPosicion(actual.getPosicion().down());
		coches.put("V", actual);
	}
	
	private boolean posicionLibre(XYLocation posAc)
	{
		boolean valida = true;
		if(posi==null) return false;
		if(posi.getXCoOrdinate()<=0 && posi.getXCoOrdinate() > tamTab.getXCoOrdinate())
			return false;
		if(posi.getYCoOrdinate()<=0 && posi.getYCoOrdinate() > tamTab.getYCoOrdinate())
			return false;
		
		Collection<Coche> tmp = coches.values();
		Iterator<Coche> it = tmp.iterator();
		while(it.hasNext() && valida)
		{
			actual = it.next();
			posi=actual.getPosicion();
			for(int i=0;i<=actual.getAncho();i++)
			{
				posi=posi.right();
				valida = !posi.equals(posAc);
			}
			posi=actual.getPosicion();
			for(int i=0;i<actual.getAlto();i++)
			{
				posi=posi.down();
				valida = !posi.equals(posAc);
			}
		}
		return valida;
	}
	
	public boolean movimientoValido(Action where) {
		posi = null;
		if (where.equals(Rd)) {
			actual = coches.get("R");
			posi = actual.getPosicion().right();
			for(int i=0;i<actual.getAncho();i++)
			{
				posi = posi.right();
			}			
		}
		else if (where.equals(Ri)) {
			actual = coches.get("R");
			posi = actual.getPosicion().left();		
		}else if (where.equals(Va)) {
			actual = coches.get("V");
			posi = actual.getPosicion().up();
			for(int i=0;i<actual.getAlto();i++)
			{
				posi = posi.up();
			}			
		}
		else if (where.equals(Vv)) {
			actual = coches.get("V");
			posi = actual.getPosicion().down();		
		}
		return posicionLibre(posi);
	}
		
	public boolean equals(Object o) {
		/* TODO
		if (this == o) {
			return true;
		}
		if ((o == null) || (this.getClass() != o.getClass())) {
			return false;
		}
		EstadoRetoAtasco aBoard = (EstadoRetoAtasco) o;

		for (int i = 0; i < 8; i++) {
			if (this.getPositionOf(i) != aBoard.getPositionOf(i)) {
				return false;
			}
		}*/
		return true;
	}
	
	@Override
	public int hashCode() {
		
		int result = 17;
		/* TODO
		for (int i = 0; i < 8; i++) {
			int position = this.getPositionOf(i);
			result = 37 * result + position;
		}*/
		return result;
	}
	
	private static void Puzle8Breadth() {
		System.out.println("\nPuzle 8 busqueda en anchura -->");
		try {
			Problem problem = new Problem(new EstadoRetoAtasco(), RetoAtascoFunctionFactory
					.getActionsFunction(), RetoAtascoFunctionFactory
					.getResultFunction(), new RetoAtascoGoalTest());
			Search search = new BreadthFirstSearch(new TreeSearch());
			SearchAgent agent = new SearchAgent(problem, search);
			printActions(agent.getActions());
			printInstrumentation(agent.getInstrumentation());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void RetoAtascoDepthLimited() {
		System.out.println("\nReto Atasco busqueda en profundidad fija (9) -->");
		try {
			Problem problem = new Problem(new EstadoRetoAtasco(), RetoAtascoFunctionFactory
					.getActionsFunction(), RetoAtascoFunctionFactory
					.getResultFunction(), new RetoAtascoGoalTest());
			Search search = new DepthLimitedSearch(9);
			SearchAgent agent = new SearchAgent(problem, search);
			printActions(agent.getActions());
			printInstrumentation(agent.getInstrumentation());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void RetoAtascoIterativeDeepening() {
		System.out.println("\nReto Atasco busqueda en profundidad iterativa -->");
		try {
			Problem problem = new Problem(new EstadoRetoAtasco(), RetoAtascoFunctionFactory
					.getActionsFunction(), RetoAtascoFunctionFactory
					.getResultFunction(), new RetoAtascoGoalTest());
			Search search = new IterativeDeepeningSearch();
			SearchAgent agent = new SearchAgent(problem, search);
			printActions(agent.getActions());
			printInstrumentation(agent.getInstrumentation());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void RetoAtascoAStarTreeMisplacedTilleHeuristic() {
		System.out.println("\nReto Atasco A* Tree Search (Misplaced Tille Heursitic)-->");
		try {
			Problem problem = new Problem(new EstadoRetoAtasco(), RetoAtascoFunctionFactory
					.getActionsFunction(), RetoAtascoFunctionFactory
					.getResultFunction(), new RetoAtascoGoalTest());
			Search search = new AStarSearch(new TreeSearch(),
					new Puzle8hMisplaced()); //TODO cambiar heuristica
			SearchAgent agent = new SearchAgent(problem, search);
			printActions(agent.getActions());
			printInstrumentation(agent.getInstrumentation());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void RetoAtascoAStarGraphMisplacedTilleHeuristic() {
		System.out.println("\nReto Atasco A* Graph Search (Misplaced Tille Heursitic)-->");
		try {
			Problem problem = new Problem(new EstadoRetoAtasco(), RetoAtascoFunctionFactory
					.getActionsFunction(), RetoAtascoFunctionFactory
					.getResultFunction(), new RetoAtascoGoalTest());
			Search search = new AStarSearch(new GraphSearch(),
					new Puzle8hMisplaced()); //TODO cambiar heuristica
			SearchAgent agent = new SearchAgent(problem, search);
			printActions(agent.getActions());
			printInstrumentation(agent.getInstrumentation());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void RetoAtascoAStarTreeManhattanHeursitic() {
		System.out.println("\nReto Atasco A* Tree Search (Manhattan Heursitic)-->");
		try {
			Problem problem = new Problem(new EstadoRetoAtasco(), RetoAtascoFunctionFactory
					.getActionsFunction(), RetoAtascoFunctionFactory
					.getResultFunction(), new RetoAtascoGoalTest());
			Search search = new AStarSearch(new TreeSearch(),
					new Puzle8hManhattan()); //TODO cambiar heuristica
			SearchAgent agent = new SearchAgent(problem, search);
			printActions(agent.getActions());
			printInstrumentation(agent.getInstrumentation());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void RetoAtascoAStarGraphManhattanHeursitic() {
		System.out.println("\nReto Atasco A* Graph Search (Manhattan Heursitic)-->");
		try {
			Problem problem = new Problem(new EstadoRetoAtasco(), RetoAtascoFunctionFactory
					.getActionsFunction(), RetoAtascoFunctionFactory
					.getResultFunction(), new RetoAtascoGoalTest());
			Search search = new AStarSearch(new GraphSearch(),
					new Puzle8hManhattan()); //TODO cambiar heuristica
			SearchAgent agent = new SearchAgent(problem, search);
			printActions(agent.getActions());
			printInstrumentation(agent.getInstrumentation());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void printInstrumentation(Properties properties) {
		Iterator<Object> keys = properties.keySet().iterator();
		while (keys.hasNext()) {
			String key = (String) keys.next();
			String property = properties.getProperty(key);
			System.out.println(key + " : " + property);
		}
	}

	private static void printActions(List<Action> actions) {
		for (int i = 0; i < actions.size(); i++) {
			String action = actions.get(i).toString();
			System.out.println(action);
		}
	}
	
	public static void main(String [] args) {
		Puzle8Breadth();
		RetoAtascoDepthLimited();
		RetoAtascoIterativeDeepening();
		RetoAtascoAStarTreeMisplacedTilleHeuristic();
		RetoAtascoAStarGraphMisplacedTilleHeuristic();
		RetoAtascoAStarTreeManhattanHeursitic();
		RetoAtascoAStarGraphManhattanHeursitic();
	}
}
